package com.xh3140.android.widget.dialog;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.Size;
import androidx.annotation.StyleRes;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import com.xh3140.android.widget.R;
import com.xh3140.android.widget.dialog.callback.ConfigParamsCallback;
import com.xh3140.android.widget.dialog.params.DialogParams;
import com.xh3140.android.widget.dialog.utils.DrawableUtils;
import com.xh3140.android.widget.dialog.view.FooterView;
import com.xh3140.android.widget.dialog.view.RootView;

/**
 * 抽象对话框类
 */
@SuppressWarnings("unused")
public abstract class AbstractDialog<V extends View> extends DialogFragment {
    /**
     * 对话框属性
     */
    private final DialogParams mDialogParams;

    /**
     * 根布局视图
     */
    @Nullable
    private RootView<V> mRootView;

    public AbstractDialog(@NonNull DialogParams params) {
        mDialogParams = params;
    }

    /**
     * 获取根布局视图
     * 注意在{@code onViewCreated()}之前，返回{@code null}
     */
    @Nullable
    public RootView<V> getRootView() {
        return mRootView;
    }

    @NonNull
    public RootView<V> requireRootView() {
        RootView<V> rootView = getRootView();
        if (rootView == null) {
            throw new IllegalStateException("AbstractDialog " + this + " does not have a RootView<?>.");
        }
        return rootView;
    }

    /**
     * 创建根视图，它包含所有的显示部件
     *
     * @param context 上下文
     */
    @NonNull
    protected abstract RootView<V> initRootView(@NonNull Context context);

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        final RootView<V> rootView = initRootView(requireContext());
        rootView.setShowDividers(mDialogParams.showDividers);
        rootView.setDividerDrawable(DrawableUtils.divider(mDialogParams.dividerColor, mDialogParams.dividerThickness, mDialogParams.alpha));
        final FooterView footerView = rootView.getFooterView();
        if (footerView != null) {
            footerView.setDividerDrawable(rootView.getDividerDrawable());
        }
        mRootView = rootView;
        return rootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 设置对话框风格，无标题，无边框
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public void onStart() {
        final Dialog dialog = requireDialog();
        // 调整对话框高度
        final View view = requireView();
        view.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
            final int maxHeight;
            if (mDialogParams.width > 1f) {
                maxHeight = (int) mDialogParams.maxHeight;
            } else {
                maxHeight = (int) (mDialogParams.maxHeight * getResources().getDisplayMetrics().heightPixels);
            }
            if (view.getHeight() > maxHeight) {
                view.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, maxHeight));
            }
        });
        // 设置对话框外部触摸关闭
        dialog.setCanceledOnTouchOutside(mDialogParams.canceledOnTouchOutside);
        // 设置对话框返回键关闭
        dialog.setCancelable(mDialogParams.cancelable);
        // 设置窗口属性，当如果Activity是可见的
        final Window window = dialog.getWindow();
        if (window != null) {
            // 设置对话框背景
            window.setBackgroundDrawable(DrawableUtils.gradient(mDialogParams.backgroundColor, mDialogParams.radius, mDialogParams.alpha));
            // 背景是否昏暗
            if (mDialogParams.isDimEnabled) {
                window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            } else {
                window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            }
            // 设置对话框位置
            final WindowManager.LayoutParams params = window.getAttributes();
            params.gravity = mDialogParams.gravity;
            params.x = mDialogParams.offsetX;
            params.y = mDialogParams.offsetY;
            // 设置对话框外边距
            final int[] margins = mDialogParams.margins;
            if (margins != null) {
                params.width = WindowManager.LayoutParams.MATCH_PARENT;
                // 对话框的外边距是用DecorView的外边距来模拟的
                window.getDecorView().setPaddingRelative(margins[0], margins[1], margins[2], margins[3]);
            } else {
                // 设置宽度
                if (mDialogParams.width > 1f) {
                    params.width = (int) mDialogParams.width;
                } else {
                    params.width = (int) (mDialogParams.width * getResources().getDisplayMetrics().widthPixels);
                }
            }
            window.setAttributes(params);
            // 进场动画和退场动画
            if (mDialogParams.windowAnimations == 0) {
                switch (params.gravity) {
                    case Gravity.CENTER:
                        window.setWindowAnimations(R.style.animation_dialog_center);
                        break;
                    case Gravity.TOP:
                        window.setWindowAnimations(R.style.animation_dialog_top);
                        break;
                    case Gravity.BOTTOM:
                        window.setWindowAnimations(R.style.animation_dialog_bottom);
                        break;
                    default:
                        break;
                }
            } else {
                window.setWindowAnimations(mDialogParams.windowAnimations);
            }
        }
        super.onStart();
    }

    @Override
    public void onDismiss(@NonNull DialogInterface dialog) {
        super.onDismiss(dialog);
        // 删除对话框Fragment
        final FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
        transaction.remove(this);
        transaction.addToBackStack(null);
    }

    @Override
    public void show(@NonNull FragmentManager manager, @Nullable String tag) {
        final FragmentTransaction transaction = manager.beginTransaction();
        if (isAdded()) {
            transaction.remove(this).commit();
        }
        // 添加对话框Fragment
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.add(this, tag);
        transaction.commitAllowingStateLoss();
    }

    /**
     * 重载显示
     *
     * @param tag 标签
     * @see DialogParams#DialogParams(FragmentActivity)
     * @see DialogParams#DialogParams(Fragment)
     */
    public void show(@Nullable String tag) {
        if (mDialogParams.manager == null) {
            throw new NullPointerException("FragmentManager is null.");
        }
        show(mDialogParams.manager, tag);
    }

    /**
     * 重载显示
     *
     * @see DialogParams#DialogParams(FragmentActivity)
     * @see DialogParams#DialogParams(Fragment)
     */
    public void show() {
        show(null);
    }

    /**
     * 弹出输入法
     */
    protected void showSoftInput() {
        final Window window = requireDialog().getWindow();
        if (window != null) {
            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        }
    }

    /**
     * 设置位置
     */
    protected void setLocation(int x, int y) {
        final Window window = requireDialog().getWindow();
        if (window != null) {
            final WindowManager.LayoutParams params = window.getAttributes();
            mDialogParams.offsetX = x;
            params.x = mDialogParams.offsetX;
            mDialogParams.offsetY = y;
            params.y = mDialogParams.offsetY;
            window.setAttributes(params);
        }
    }

    /**
     * 重置大小
     */
    protected void resize() {
        final Window window = requireDialog().getWindow();
        if (window != null) {
            final WindowManager.LayoutParams params = window.getAttributes();
            final int minimum = getResources().getDisplayMetrics().widthPixels / 3;
            if (mDialogParams.width < minimum) {
                params.width = minimum;
            } else {
                params.width = (int) mDialogParams.width;
            }
            window.setAttributes(params);
        }
    }

    /**
     * 构建器
     * 提供对话框属性的配置
     */
    public static abstract class Builder<D extends AbstractDialog<?>, B extends Builder<D, B>> {
        /**
         * 对话框属性
         */
        protected final DialogParams mDialogParams;

        public Builder(@NonNull Context context) {
            mDialogParams = new DialogParams(context);
        }

        public Builder(@NonNull FragmentActivity activity) {
            mDialogParams = new DialogParams(activity);
        }

        public Builder(@NonNull Fragment fragment) {
            mDialogParams = new DialogParams(fragment);
        }

        /**
         * 转换为继承可用链式编程构建器
         *
         * @return 可以链式编程的构建器
         */
        @SuppressWarnings("unchecked")
        private B toExtendsChainBuilder() {
            return (B) this;
        }

        /**
         * 构建对话框
         * 这个方法应该在所有配置结束后调用
         */
        public abstract D build();

        /**
         * 配置对话框属性
         *
         * @param callback 配置回调
         */
        public B configDialogParams(@NonNull ConfigParamsCallback<DialogParams> callback) {
            callback.callback(mDialogParams);
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框位置
         * 这个位置是相对于应用窗口的
         *
         * @param gravity 位置
         */
        public B setDialogGravity(int gravity) {
            mDialogParams.gravity = gravity;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框背景是否昏暗
         * true 对话框背景昏暗
         * false 对话框背景不昏暗
         *
         * @param enabled 标识
         */
        public B setDialogDimEnabled(boolean enabled) {
            mDialogParams.isDimEnabled = enabled;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框返回键是否关闭
         * true 表示按下应用返回键时对话框会触发关闭
         * false 表示按下应用返回键时对话框不会触发关闭
         *
         * @param cancelable 是否关闭
         */
        public B setDialogCanceledBack(boolean cancelable) {
            mDialogParams.cancelable = cancelable;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框是否点击外部关闭
         * true 表示点击对话框外部时对话框会触发关闭
         * false 表示点击对话框外部时对话框不会触发关闭
         *
         * @param canceled 是否关闭
         */
        public B setDialogCanceledOnTouchOutside(boolean canceled) {
            mDialogParams.canceledOnTouchOutside = canceled;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框透明度，范围：0-255；255不透明
         * 范围：0 - 255
         * 值越小表示越透明
         * 0x00 完全透明
         * 0xFF 完全不透明
         *
         * @param alpha 透明度百分比
         */
        public B setDialogAlpha(@IntRange(from = 0x00, to = 0xFF) int alpha) {
            mDialogParams.alpha = alpha;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框透明度，范围：0-1；1不透明
         * 范围：0.0F - 1.0F
         * 值越小表示对话框越透明
         * 0.0F 对话框完全透明
         * 1.0F 对话框完全不透明
         *
         * @param alpha 透明度百分比
         */
        public B setDialogAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
            mDialogParams.alpha = (int) (0xFF * alpha);
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框宽度
         * 在0-1范围内：表示的是宽度相对窗口宽度所占的百分比
         * 大于1时：表示宽度的真实像素值
         *
         * @param width 宽度
         */
        public B setDialogWidth(float width) {
            mDialogParams.width = width;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框最大高度
         * 在0-1范围内：表示的是最大高度相对窗口高度所占的百分比
         * 大于1时：表示最大高度的真实像素值
         * 当的实际高度大于这个高度时，会被限制在这个高度值
         *
         * @param maxHeight 最大高度
         */
        public B setDialogMaxHeight(float maxHeight) {
            mDialogParams.maxHeight = maxHeight;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框窗口动画
         *
         * @param resId 动画资源ID
         */
        public B setDialogWindowAnimations(@StyleRes int resId) {
            mDialogParams.windowAnimations = resId;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框背景颜色 argb
         *
         * @param color 背景颜色
         */
        public B setDialogBackgroundColor(@ColorInt int color) {
            mDialogParams.backgroundColor = color;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框分割线显示方式
         * 如果这个值小于0，分割线只会在适当的时候显示在按钮上方
         *
         * @param showDividers 分割线显示方式
         */
        public B setDialogShowDividers(int showDividers) {
            mDialogParams.showDividers = showDividers;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框分割线颜色 argb
         *
         * @param color 分割线颜色
         */
        public B setDialogDividerColor(@ColorInt int color) {
            mDialogParams.dividerColor = color;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框分割线厚度 px
         *
         * @param thickness 分割线厚度
         */
        public B setDialogDividerThickness(@Px int thickness) {
            mDialogParams.dividerThickness = thickness;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框外边距
         * 格式：[start, top, end, bottom] px
         *
         * @param margins 外边距
         */
        public B setDialogMargins(@Nullable @Size(4) int[] margins) {
            mDialogParams.margins = margins;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框外边距
         *
         * @param margins 外边距 px
         */
        public B setDialogMargins(@Px int margins) {
            mDialogParams.margins = new int[]{margins, margins, margins, margins};
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框外边距
         *
         * @param left   左边距 px
         * @param top    上边距 px
         * @param right  右边距 px
         * @param bottom 下边距 px
         */
        public B setDialogMargins(@Px int left, @Px int top, @Px int right, @Px int bottom) {
            mDialogParams.margins = new int[]{left, top, right, bottom};
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框的圆角半径 px
         *
         * @param radius 圆角半径
         */
        public B setDialogRadius(int radius) {
            mDialogParams.radius = radius;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框X坐标偏移 px
         *
         * @param x X坐标
         */
        public B setDialogX(int x) {
            mDialogParams.offsetX = x;
            return toExtendsChainBuilder();
        }

        /**
         * 设置对话框Y坐标偏移 px
         *
         * @param y Y坐标
         */
        public B setDialogY(int y) {
            mDialogParams.offsetY = y;
            return toExtendsChainBuilder();
        }
    }
}
